if ( !FBDEditor ) FBDEditor = {};

FBDEditor.controller = function ( model, view, win, FBEditor, demo ) {
/*
    controller for FBDEditor 1.0
    Copyright (C) 2008-2009 Yuriy Butenko y.v.butenko@gmail.com

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
	var ctxt = demo.controller = this;
	model = new model();
	var view = new view( document.body, new yWin( document.body ), this, FBEditor );
	var fileName = "schema";
	var fileExt = ".fbd";
	var linguages = [];
	for ( var i in model ) {
		if ( i.indexOf("CodeCreator") != -1 ) linguages.push(i.replace("CodeCreator",""));
	};
	view.addFBButton( 'variable' );
	view.addFBButton( 'constant' );
	view.addFBButton( 'input' );
	view.addFBButton( 'output' );
	var snap = function ( x ) {
		return x + 4 >> 3 << 3;
	};
	var testNeg = function ( m ) {
		if ( model.getX(m) >= 0 && model.getY(m) >= 0 ) return;
		view.del( model.getVHandle( m ) );
		model.del( m );
	};
	this.send = function ( msg ) {
//		var s = "";for ( var i = 0, l = arguments.length; i < l; i++ ) s += arguments[i] + ", ";alert(s);
		switch ( msg ) {
			case 'tbtnclick':
				switch ( arguments[3] ) {
					case "trace":
						setMode( trace );
						break;
					case "del":
						setMode( del );
						break;
					case "move":
						setMode( move );
						break;
					case "copy":
						setMode( copy );
						break;
					case "edit":
						setMode( edit );
						break;
					case "help":
						view.displayHelp();
						view.message( "help" );
						return;
					case "about":
						view.displayAbout();
						view.message( "about" );
						return;
					case 'fb':
						setMode();
						view.displayFBEditor();
						break;
					case 'load':
						setMode();
						loadFile();
						break;
					case 'save':
						setMode();
						saveFile();
						break;
					case 'generate':
						setMode();
						generation();
						break;
				};
				break;
			case 'btnclick':
				insertFB.newName = arguments[3];
				insertFB.point = {"x":arguments[1],"y":arguments[2]};
				setMode( insertFB );
				break;
			case 'okFBEdit':
				model.setBlocksDef( view.getDefOutFBEditor(), this );
				view.hideFBEditor();
				break;
			case 'acceptFBEdit':
				model.setBlocksDef( view.getDefOutFBEditor(), this );
				break;
			case 'cancelFBEdit':
				var def = view.getDefOutFBEditor();
				for ( var i in def ) view.delFBOutFBEditor( i );	//clear
				var def = model.getBlocksDef();
				for ( var i in def ) view.addFBToFBEditor( i, def[i].inputs, def[i].outputs );	//load
				view.hideFBEditor();
				break;
		};
	};
// methods for exchange model blocks def
	this.redrawFB = function ( x, y, id, classId, inputs, outputs, h ) {
		var atr = view.getAttr( h );
		view.del( h );
		var res = view.drawFB( x, y, id, classId, inputs, outputs );
		view.setAttr( res, atr );
		return res;
	};
	this.delEntity = function ( vHandle ) {
		view.del( vHandle );
	};
	this.addNewBlock = function ( id ) {
		view.addFBButton( id );
	};
	this.delOldBlock = function ( id ) {
		view.delFBButton( id );
	};
//		var s = "";for ( var i in def ) s += i + ": " + def[i]+", ";alert(s);
/* ---- ---- ---- ---- ---- ---- ---- */
	var select = function ( selectFunction ) {
		select.selectFunction = selectFunction;
		view.message( "select entities" );
		view.selection( select.oneSelect );
		view.getPoint( select.startFrame );
	};
	select.oneSelect = function ( e, p ) {
		select.terminate();
		select.selectFunction( e, p );
	};
	select.startFrame = function ( p ) {
		view.getPoint();
		view.selection();
		if ( !p ) {
			select.terminate();
			select.selectFunction( p );
			return;
		};
		view.message( "select: enter second point" );
		select.p0 = p;
		view.redrawFrame( view.onEntity( select.frame ), p.x, p.y, p.x, p.y );
		view.upEntity( select.frame );
		view.getPoint( select.endFrame, select.dragFrame );
	};
	select.endFrame = function ( p ) {
		view.getPoint();
		if ( !p ) {
			select( select.selectFunction );
			return;
		};
		view.getPoint();
		var sel = model.select( select.p0.x, select.p0.y, p.x, p.y );
		view.offEntity( select.frame );
		if ( sel.length ) select.selectFunction( sel );
		else {
			view.message( "select: 0 selected" );
			select( select.selectFunction );
		};
	};
	select.dragFrame = function ( p ) {
		view.redrawFrame( select.frame, select.p0.x, select.p0.y, p.x, p.y );
	};
	select.terminate = function () {
		view.getPoint();
		view.selection();
		view.offEntity( select.frame );
	};
	select.frame = view.drawFrame(0,0,0,0);
	view.offEntity( select.frame );
/* ---- ---- ---- ---- ---- ---- ---- */
	var selection = [];
	var selectMode = function () {
		select( selectMode.subMode );
	};
	selectMode.subMode = function ( sel ) {
		if ( !sel ) {	// terminate
			unselectAll();
		} else if ( arguments.length == 2 ) {	// single
			if ( view.isSelect( sel ) ) {
				view.unselect( sel );
				for ( var i = 0, l = selection.length; i < l; i++ )
					if ( selection[i] == sel ) {
						selection.splice(i,1);
						view.message( "1 entitiy removed" );
					};
//				selectMode();
			} else {
				view.select( sel );
				selection.push( sel );
				view.message( "1 entitiy added" );
			};
		} else {	// multiple
			var j, oldLength = selection.length;
			while ( sel.length )
				if ( !view.isSelect( j = sel.pop() ) )
					selection.push( view.select( j ) );
			view.message( ( selection.length - oldLength ) + " entities added" );
		};
		select( selectMode.subMode );
	};
	selectMode.terminate = function () {
		select.terminate();
	};
	var unselectAll = function () {
		while ( selection.length ) view.unselect( selection.pop() );
	};
/* ---- ---- ---- ---- ---- ---- ---- */
	var insertFB = function () {	// require set lastPoint
		unselectAll();
		insertFB.point.x = snap(insertFB.point.x)-16;
		insertFB.point.y = snap(insertFB.point.y)-8;
		view.selectFBButton( insertFB.name1 = insertFB.newName );	// Safari not understand 'name'
		view.select( insertFB.block = insertFB.drawBlock( insertFB.point.x, insertFB.point.y ) );
		view.message( "insert: enter a point" );
		view.getPoint( insertFB.subMode, insertFB.moveBlock );
	};
	insertFB.moveBlock = function ( p ) {
		view.moveTo( insertFB.block, snap(p.x)-16, snap(p.y)-8 );
	};
	insertFB.subMode = function ( p ) {
		if ( !p ) {
			setMode();
			return;
		};
		insertFB.point.x = snap(p.x)-16;
		insertFB.point.y = snap(p.y)-8;
		view.editProperty(
			insertFB.name1 != "constant" ? "Enter identifier" : "Enter constant value",
			(insertFB.name1 == "constant" ? 0 :
				insertFB.name1 == "input" ? "input" + insertFB.counterI++ :
				insertFB.name1 == "output" ? "output" + insertFB.counterO++ :
				insertFB.name1 == "variable" ? "var" + insertFB.counterV++ :
				"fb" + insertFB.counterF++ ),
			null,
			insertFB.setAndContinue
		);
	};
	insertFB.setAndContinue = function ( txt ) {
		view.message( "done" );
		txt && view.setText( insertFB.block, txt );
		view.setAttr( insertFB.block,
			(insertFB.name1 == "constant" ? model.createConsta(insertFB.point.x,insertFB.point.y,txt,insertFB.block) :
			insertFB.name1 == "input" ? model.createInput(insertFB.point.x,insertFB.point.y,txt,insertFB.block) :
			insertFB.name1 == "output" ? model.createOutput(insertFB.point.x,insertFB.point.y,txt,insertFB.block) :
			insertFB.name1 == "variable" ? model.createVari(insertFB.point.x,insertFB.point.y,txt,insertFB.block) :
			model.createFB(insertFB.point.x,insertFB.point.y,txt,insertFB.name1,insertFB.block))
		);
		view.unselect( insertFB.block );
		insertFB.point.x += 16;
		insertFB.point.y += 8;
		if ( txt && ( txt.toLowerCase() == "init" || txt.toLowerCase() == "eval" ) ) {
			view.alert( "&quot;"+txt+"&quot; name is no good." );
		};
		insertFB();
	};
	insertFB.terminate = function () {
		view.del( insertFB.block );
		view.unselectFBButton( insertFB.name1 );
		view.getPoint();
	};
	insertFB.drawBlock = function ( x, y ) {
		return insertFB.name1 == "variable" ? view.drawVari( x, y, "?" ) :
			insertFB.name1 == "constant" ? view.drawConsta( x, y, "?" ) :
			insertFB.name1 == "input" ? view.drawInput( x, y, "?" ) :
			insertFB.name1 == "output" ? view.drawOutput( x, y, "?" ) :
			view.drawFB( x,
				y,
				"?",
				insertFB.name1,
				model.getBlockDef(insertFB.name1).inputs,
				model.getBlockDef(insertFB.name1).outputs
			);
	};
	insertFB.name1 = "";
	insertFB.counterI = 1;
	insertFB.counterO = 1;
	insertFB.counterV = 1;
	insertFB.counterF = 1;
/* ---- ---- ---- ---- ---- ---- ---- */
	var trace = function () {
		unselectAll();
		view.selectTButton( "trace" );
		view.message( "trace: enter first point" );
		view.getPoint( trace.getSecondPoint );
	};
	trace.getSecondPoint = function ( p ) {
		view.getPoint();
		if ( !p ) {
			setMode();
			return;
		};
		trace.p0 = {"x":snap(p.x),"y":snap(p.y)};
		trace.trace = view.drawTrace( p.x, p.y, p.x, p.y, -1 );
		view.select( trace.trace );
		view.message( "trace: enter second point" );
		view.getPoint( trace.doneDraw, trace.drag );
	};
	trace.drag = function ( p ) {
		view.redrawTrace( trace.trace, trace.p0.x, trace.p0.y, snap(p.x), snap(p.y), -1 );
		view.select( trace.trace );
	};
	trace.doneDraw = function ( p ) {
		view.getPoint();
		if ( !p ) {
			trace.terminate();
			trace();
			return;
		};
		view.setAttr( trace.trace, model.createTrace( trace.p0.x, trace.p0.y, snap(p.x), snap(p.y), -1, trace.trace ) );
		view.unselect( trace.trace );
		trace.trace = null;
		trace();
	};
	trace.terminate = function () {
		trace.trace && view.del( trace.trace );
		trace.trace = null;
		view.unselectTButton( "trace" );
		view.getPoint();
	};
/* ---- ---- ---- ---- ---- ---- ---- */
	var del = function () {
		view.selectTButton( "del" );
		if ( selection.length ) {
			del.del( selection );
		};
		select( del.newSelect );
	};
	del.newSelect = function ( sel ) {
		del.del( arguments.length == 1 ? sel : [sel] );
		select( del.newSelect );
	};
	del.terminate = function () {
		view.unselectTButton( "del" );
		view.message( "delete terminated" );
		select.terminate();
	};
	del.del = function ( sel ) {
		var j, l = sel.length;
		while ( sel.length ) {
			model.del( view.getAttr( j = sel.pop() ) );
			view.del( j );
		};
		view.message( "" + l + " deleted" );
	};
/* ---- ---- ---- ---- ---- ---- ---- */
	var move = function () {
		view.selectTButton( "move" );
		if ( selection.length ) {
			view.message( "move: select first point" );
			view.getPoint( move.getFirstPoint );
		} else {
			view.message( "move: select entities" );
			select( move.select );
		};
	};
	move.select = function ( sel ) {
		select.terminate();
		if ( !sel ) {	//terminate
			setMode();
		} else if ( arguments.length == 2 ) {	//single
			selection = [ sel ];
			view.select( sel );
			move.getFirstPoint( arguments[1] );
		} else {	//multiple
			view.message( "move: enter first point" );
			selection = sel;
			for ( var i = sel.length; i--; ) view.select( sel[i] );
			view.getPoint( move.getFirstPoint );
		};
	};
	move.getFirstPoint = function ( p ) {
		view.getPoint();
		if ( !p ) {
			unselectAll();
			move();
			return;
		};
		move.firstPoint = p;
		move.cont = view.initContainer( selection );
		view.message( "move: enter second point" );
		view.getPoint( move.getSecondPoint, move.drag );
	};
	move.getSecondPoint = function ( p ) {
		view.getPoint();
		view.deinitContainer();
		move.cont = null;
		if ( p ) {
			var dX = snap(p.x - move.firstPoint.x);
			var dY = snap(p.y - move.firstPoint.y);
			for ( var i = selection.length; i--; ) {
				testNeg( model.moveBy( view.getAttr( view.moveBy( selection[i], dX, dY ) ), dX, dY ) );
			};
		};
		unselectAll();	// if entity is deleted (testNeg), entity is in selection
		move();
	};
	move.drag = function ( p ) {
		view.moveTo( move.cont, snap(p.x - move.firstPoint.x), snap(p.y - move.firstPoint.y) );
	};
	move.terminate = function () {
		move.cont && view.deinitContainer( move.cont );
		select.terminate();
		unselectAll();
		view.getPoint();
		view.unselectTButton( "move" );
	};
/* ---- ---- ---- ---- ---- ---- ---- */
	var copy = function () {
		view.selectTButton( "copy" );
		if ( selection.length ) {
			view.message( "copy: select first point" );
			view.getPoint( copy.getFirstPoint );
		} else {
			view.message( "copy: select entities" );
			select( copy.select );
		};
	};
	copy.select = function ( sel ) {
		select.terminate();
		if ( !sel ) {	//terminate
			setMode();
		} else if ( arguments.length == 2 ) {	//single
			selection = [ sel ];
			view.select( sel );
			copy.getFirstPoint( arguments[1] );
		} else {	//multiple
			view.message( "copy: enter first point" );
			selection = sel;
			for ( var i = sel.length; i--; ) view.select( sel[i] );
			view.getPoint( copy.getFirstPoint );
		};
	};
	copy.getFirstPoint = function ( p ) {
		view.getPoint();
		if ( !p ) {
			unselectAll();
			copy();
			return;
		};
		copy.firstPoint = p;
		copy.sel = [];
		for ( var i = 0, l = selection.length; i < l; i++ ) {
			view.unselect( selection[i] );
			copy.sel[i] = view.select( view.copy( selection[i] ) );
		};
		copy.cont = view.initContainer( copy.sel );
		view.message( "copy: enter second point" );
		view.getPoint( copy.getSecondPoint, copy.drag );
		copy.drag( arguments[1] || p );
	};
	copy.getSecondPoint = function ( p ) {
		view.getPoint();
		if ( !p ) {
			copy.sel = null;
			view.delContainer( copy.cont );
			copy.cont = null;
			unselectAll();
			copy();
			return;
		};
		view.deinitContainer( copy.cont );
		copy.cont = null;
		var dX = snap(p.x - copy.firstPoint.x);
		var dY = snap(p.y - copy.firstPoint.y);
		for ( var v, m, i = selection.length; i--; ) {
			v = view.unselect( copy.sel[i] );
			m = model.copy( view.getAttr( selection[i] ) );
			view.setAttr( v, m );
			model.setVHandle( m, v );
			view.moveBy( v, dX, dY )
			model.moveBy( m, dX, dY );
			testNeg( m );
		};
		copy.getFirstPoint( copy.firstPoint, p );
	};
	copy.drag = function ( p ) {
		view.moveTo( copy.cont, snap(p.x - copy.firstPoint.x), snap(p.y - copy.firstPoint.y) );
	};
	copy.terminate = function () {
		copy.cont && view.delContainer( copy.cont );
		copy.sel = null;
		select.terminate();
		unselectAll();
		view.getPoint();
		view.unselectTButton( "copy" );
	};
/* ---- ---- ---- ---- ---- ---- ---- */
	var edit = function () {
		view.selectTButton( "edit" );
		if ( selection.length ) {
			edit.select(selection);
		} else {
			view.message( "edit: select entities" );
			select( edit.select );
		};
	};
	edit.select = function ( sel ) {
		select.terminate();
		if ( !sel ) {	//terminate
			setMode();
		} else if ( arguments.length == 2 ) {	//single
			if ( model.getType( view.getAttr( sel ) ) == "trace" ) editTrace( sel, arguments[1] );
			else {
				selection = [ sel ];
				edit.ask();
			};
		} else {	//multiple
			selection = sel;
			for ( var i = sel.length; i--; ) view.unselect( sel[i] );
			edit.ask();
		};
	};
	edit.ask = function () {
		if ( !selection.length ) {
			edit();
			return;
		};
		while ( selection.length
			&& model.getType( view.getAttr( selection[0] ) ) == "trace" ) selection.shift();
		if ( !selection.length ) {
			edit();
			return;
		};
		var tp = model.getType( view.getAttr( view.select( selection[0] ) ) );
		view.editProperty(
			tp != "consta" ? "Enter identifier" : "Enter constant value",
			model.getId( view.getAttr( selection[0] ) ),
			null,
			edit.setAndContinue
		);
	};
	edit.setAndContinue = function ( txt ) {
		if ( txt == null ) {
			unselectAll();
			setMode();
			return;
		};
		model.setId( view.getAttr( view.unselect( selection[0] ) ), txt );
		view.setText( selection.shift(), txt );
		edit.ask();
	};
	var editTrace = function ( t, p ) {
		var m = view.getAttr( editTrace.t = t );
		var x0 = editTrace.x0 = model.getX( m );
		var y0 = editTrace.y0 = model.getY( m );
		var x1 = editTrace.x1 = model.getX1( m );
		var y1 = editTrace.y1 = model.getY1( m );
		editTrace.l0 = model.getL0( m );
		editTrace.p = p;
		var mtd = "drag";
		if ( x0 == x1 ) {
			if ( y0 != y1 && Math.abs( y0 - p.y ) < Math.abs( y1 - p.y ) ) mtd = "dragBegin";
		} else if ( y0 == y1 ) {
			if ( Math.abs( x0 - p.x ) < Math.abs( x1 - p.x ) ) mtd = "dragBegin";
		} else {
			if ( Math.abs( y0 - p.y ) < 4 ) mtd = "dragBegin";
			else if ( Math.abs( y1 - p.y ) > 3 ) mtd = "dragMidle";
		};
		editTrace[editTrace.mtd = mtd]( p );
		view.getPoint( editTrace.stop, editTrace[mtd] );
	};
	editTrace.t = null;
	editTrace.mtd = null;
	editTrace.x0 = null;
	editTrace.y0 = null;
	editTrace.x1 = null;
	editTrace.y1 = null;
	editTrace.l0 = null;
	editTrace.tDef = null;
	editTrace.drag = function ( p ) {
		view.redrawTrace( editTrace.t, editTrace.x0, editTrace.y0, snap( p.x ), snap( p.y ), editTrace.l0 );
		view.select( editTrace.t );
	};
	editTrace.dragBegin = function ( p ) {
		view.redrawTrace( editTrace.t, snap( p.x ), snap( p.y ), editTrace.x1, editTrace.y1, editTrace.l0 );
		view.select( editTrace.t );
	};
	editTrace.evalL0 = function ( x ) {
		return Math.max( Math.min( x - editTrace.x0, Math.abs( editTrace.x1 - editTrace.x0 ) - 4 ), 4 ) >> 2 << 2;
	};
	editTrace.dragMidle = function ( p ) {
		view.redrawTrace( editTrace.t, editTrace.x0, editTrace.y0, editTrace.x1, editTrace.y1, editTrace.evalL0( p.x ) );
		view.select( editTrace.t );
	};
	editTrace.stop = function ( p ) {
		view.getPoint();
		if ( p ) {
			var mtd = editTrace.mtd;
			model.regenTrace( view.getAttr( editTrace.t ),
				mtd == "dragBegin" ? snap( p.x ) : editTrace.x0,
				mtd == "dragBegin" ? snap( p.y ) : editTrace.y0,
				mtd == "drag" ? snap( p.x ) : editTrace.x1,
				mtd == "drag" ? snap( p.y ) : editTrace.y1,
				mtd == "dragMidle" ? editTrace.evalL0( p.x ) : editTrace.l0 );
		} else {
			view.redrawTrace( editTrace.t, editTrace.x0, editTrace.y0, editTrace.x1, editTrace.y1, editTrace.l0 );
		};
		view.unselect( editTrace.t );
		edit();
	};
	edit.terminate = function () {
		select.terminate();
		unselectAll();
		view.unselectTButton( "edit" );
	};
/* ---- ---- ---- ---- ---- ---- ---- */
	var loadFile = function () {
		if ( fio.testIO() ) view.promptLoad( fileName, ctxt, loadFileNext );
		else view.textIn( loadFileNext );
	};
	var loadFileNext = function ( res ) {
		if ( res == null ) return;
		if ( fio.testIO() ) res = fio.read( ( fileName = res ) + fileExt );
		if ( !res ) view.textIn( loadFileNext2 );
		else loadFileNext2( res );
	};
	var loadFileNext2 = function ( res ) {
		if ( res && model.load( res, ctxt ) ) {
			view.message( "load: done" );
			var b = model.getBlocksDef();
			view.setDefToFBEditor( b );
		} else {
			view.message( "load: error" );
			view.alert( "<p><b>Error of read</b></p><p>file: "+fileName+fileExt+"</p>" );
		};
		setMode();
	};
	var saveFile = function () {
		if ( fio.testIO() ) view.promptSave( fileName, ctxt, saveFileNext );
		else saveFileNext( true );
	};
	var saveFileNext = function ( res ) {
		if ( res == null ) return;
		var text = model.save();
		if ( fio.testIO() ) {
			res = fio.write( ( fileName = res ) + fileExt, text );
			if ( res == text.length ) {
				view.message( "save: done" );
				setMode();
				return;
			};
//			view.message( "save: error" );
//			view.alert( "<p><b>Error of write</b></p><p>file: "+fileName+fileExt+"</p>" );
		};
		view.textOut( text, function () { setMode(); } );
	};
	this.createEntity = function ( def, m ) {
		var v;
		switch ( def.tp ) {
		// TODO test input data
			case "trace":
				v = view.drawTrace(def.x0,def.y0,def.x1,def.y1,("l0" in def ? def.l0 : -1));
				break;
			case "fb":
				var io = model.getBlockDef(def.classId);
				v = view.drawFB(def.x,def.y,def.text,def.classId,io.inputs,io.outputs);
				break;
			case "input":
				v = view.drawInput(def.x,def.y,def.text);
				break;
			case "output":
				v = view.drawOutput(def.x,def.y,def.text);
				break;
			case "consta":
				v = view.drawConsta(def.x,def.y,def.text);
				break;
			case "vari":
				v = view.drawVari(def.x,def.y,def.text);
				break;
		};
		view.setAttr( v, m );
		return v;
	};
/* ---- ---- ---- ---- ---- ---- ---- */
	var generation = function () {
		view.promptGenerator( fileName, linguages, linguages[0], ctxt, generationNext );
	};
	var generationNext = function ( fName, ling ) {
		if ( fName == null ) return;
		var data, fExt = model[ling+"CodeCreator"].fileExt;
		try {
			var mName = /[^/\\]+$/.exec(fName);
			data = model[ling+"CodeCreator"]( mName[0], model.getCode() );
		} catch ( e ) {
			view.message( "code gen: drawing error" );
			view.alert( "<p><b>drawing error</b></p>" );
			return;
		};
		if ( fio.testIO() ) {
			res = fio.write( ( fName ) + fExt, data );
			if ( res == data.length ) {
				view.message( "code gen: done" );
				setMode();
				return;
			};
		};
		view.textOut( data, function () { setMode(); } );
	};
/* ---- ---- ---- ---- ---- ---- ---- */
	this._loadSample = function ( data ) {
		if ( model.load( data, this ) ) {
			var b = model.getBlocksDef();
			view.setDefToFBEditor( b );
		};
	};
	this._moveWinTo = function ( title, x, y ) {
		view._moveWinTo( title, x, y );
	};
/* ---- ---- ---- ---- ---- ---- ---- */
	var mode = selectMode;
	var setMode = function ( m ) {
		m = m || selectMode;
		if ( mode == m && ( mode != insertFB || mode.name == mode.newName ) ) m = selectMode;
		mode.terminate();
		( mode = m )();
	};
	var runDemo = function ( answ ) {
		answ && demo.init() && demo.on();
	};

	view.message("FBDEditor: ready");
	setMode();
	view.confirm( "demo select", "Do you want to run demo mode?", this, runDemo );
}
